package cmd

import (
	"bufio"
	ckeadm "ckeadm/cmd/apis"
	ckeadmv1beta1 "ckeadm/cmd/apis/v1alpha1"
	"ckeadm/cmd/executor"
	"ckeadm/cmd/runner"
	"ckeadm/constants"
	"ckeadm/util"
	"encoding/base64"
	"fmt"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func ComponentCommand() cli.Command {
	var componentCreateFlags = []cli.Flag{
		runner.ComponentFlag,
		runner.ClusterFlag,
		runner.NodeFlag,
		runner.PrivateKey,
		runner.LogLevelFlag,
		runner.AddressFlag,
		runner.WatchPortFlag,
	}

	var componentScaleUpFlags = []cli.Flag{
		runner.ComponentFlag,
		runner.ClusterFlag,
		runner.NodeFlag,
		runner.PrivateKey,
		runner.LogLevelFlag,
		runner.AddressFlag,
		runner.WatchPortFlag,
		runner.NodeNameFlag,
	}

	var componentScaleDownFlags = []cli.Flag{
		runner.ComponentFlag,
		runner.ClusterFlag,
		runner.NodeFlag,
		runner.PrivateKey,
		runner.LogLevelFlag,
		runner.AddressFlag,
		runner.WatchPortFlag,
		runner.NodeNameFlag,
	}

	var componentDeleteFlags = []cli.Flag{
		runner.ComponentFlag,
		runner.ClusterFlag,
		runner.NodeFlag,
		runner.PrivateKey,
		runner.LogLevelFlag,
		runner.AddressFlag,
		runner.WatchPortFlag,
	}

	//var componentMaster2SlaveFlags = []cli.Flag{
	//	//	runner.ComponentFlag,
	//	//	runner.ClusterFlag,
	//	//	runner.NodeFlag,
	//	//	runner.PrivateKey,
	//	//	runner.LogLevelFlag,
	//	//	runner.AddressFlag,
	//	//	runner.WatchPortFlag,
	//	//}
	//	//
	//	//var componentSlave2MasterFlags = []cli.Flag{
	//	//	runner.ComponentFlag,
	//	//	runner.ClusterFlag,
	//	//	runner.NodeFlag,
	//	//	runner.PrivateKey,
	//	//	runner.LogLevelFlag,
	//	//	runner.AddressFlag,
	//	//	runner.WatchPortFlag,
	//	//}

	var componentUpdateFlags = []cli.Flag{
		runner.ComponentFlag,
		runner.ClusterFlag,
		runner.NodeFlag,
		runner.PrivateKey,
		runner.LogLevelFlag,
		runner.AddressFlag,
		runner.WatchPortFlag,
	}

	var componentUpgradeFlags = []cli.Flag{
		runner.ComponentFlag,
		runner.ClusterFlag,
		runner.NodeFlag,
		runner.PrivateKey,
		runner.LogLevelFlag,
		runner.AddressFlag,
		runner.WatchPortFlag,
	}

	var componentExplainFlags = []cli.Flag{
		runner.ComponentNameFlag,
	}

	var componentStatusFlags = []cli.Flag{
		runner.StatusComponentFlag,
		runner.StatusNodeFlag,
		runner.ClusterIdFlag,
		runner.ActionFlag,
	}

	//ckeadm component create
	componentSubCmd := []cli.Command{
		{
			Name:   "create",
			Usage:  "create component(s)",
			Action: componentCreateFromCli,
			Flags:  componentCreateFlags,
		},
		{
			Name:   "scaleup",
			Usage:  "scaleup component(s)",
			Action: componentScaleUpFromCli,
			Flags:  componentScaleUpFlags,
		},
		{
			Name:   "scaledown",
			Usage:  "scaledown component(s)",
			Action: componentScaleDownFromCli,
			Flags:  componentScaleDownFlags,
		},
		{
			Name:   "delete",
			Usage:  "delete component(s)",
			Action: componentDeleteFromCli,
			Flags:  componentDeleteFlags,
		},
		//{
		//	Name:   "master2slave",
		//	Usage:  "change a master node to slave node",
		//	Action: componentMaster2SlaveFromCli,
		//	Flags:  componentMaster2SlaveFlags,
		//},
		//{
		//	Name:   "slave2master",
		//	Usage:  "change a slave node to master node",
		//	Action: componentSlave2MasterFromCli,
		//	Flags:  componentSlave2MasterFlags,
		//},
		{
			Name:   "update",
			Usage:  "update component(s)",
			Action: componentUpdateFromCli,
			Flags:  componentUpdateFlags,
		},
		{
			Name:   "upgrade",
			Usage:  "upgrade component(s)",
			Action: componentUpgradeFromCli,
			Flags:  componentUpgradeFlags,
		},
		{
			Name:   "list",
			Usage:  "list all supported components",
			Action: componentListFromCli,
			Flags:  nil,
		},
		{
			Name:   "explain",
			Usage:  "show detail of the specific component",
			Action: componentExplainFromCli,
			Flags:  componentExplainFlags,
		},
		{
			Name:   "status",
			Usage:  "show log of the specific component",
			Action: componentStatusFromCli,
			Flags:  componentStatusFlags,
		},
	}

	//ckeadm component
	return cli.Command{
		Name:        "component",
		Usage:       "do some actions with the specific component",
		Subcommands: componentSubCmd,
		Action:      nil,
		Flags:       nil,
	}
}

//cke-controller调用
func componentCreateFromCli(ctx *cli.Context) error {
	logrus.Infof("running ckeadm component create...")
	logrus.SetLevel(logrus.Level(ctx.Int("v")))

	ckeClusterCode := ctx.String("cluster")
	ckeComponentCode := ctx.String("component")
	ckeNodeCode := ctx.String("node")
	keyFile := ctx.String("private-key")
	if keyFile == "" {
		keyFile = constants.DefaultKeyFile
	}
	//keyFile, err := runner.GetPrivateKeyFile(ctx.String("private-key"))
	//if err != nil {
	//	logrus.Errorf("error while get key file %s, err: %s", ctx.String("private-key"), err.Error())
	//	return fmt.Errorf("error while get key file %s, err: %s", ctx.String("private-key"), err.Error())
	//}
	var ckeCluster ckeadmv1beta1.CkeCluster
	ckeNodes := new([]ckeadmv1beta1.CkeNode)
	ckeComponents := new([]ckeadmv1beta1.CkeComponent)

	ckeClusterByte, err := base64.StdEncoding.DecodeString(ckeClusterCode)
	if err != nil {
		logrus.Errorf("error decode CkeCluster base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeCluster base64 code: %s", err.Error())
	}
	err = runner.GetCkeClusterFromYaml(ckeClusterByte, &ckeCluster)
	if err != nil {
		logrus.Errorf("error unmarshal ckeCluster: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeCluster: %s", err.Error())
	}

	ckeComponentByte, err := base64.StdEncoding.DecodeString(ckeComponentCode)
	if err != nil {
		logrus.Errorf("error decode CkeComponent base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeComponent base64 code: %s", err.Error())
	}
	err = runner.GetCkeComponentFromYaml(ckeComponentByte, ckeComponents)
	if err != nil {
		logrus.Errorf("error unmarshal ckeComponent: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeComponent: %s", err.Error())
	}

	ckeNodesByte, err := base64.StdEncoding.DecodeString(ckeNodeCode)
	if err != nil {
		logrus.Errorf("error decode CkeNode base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeNode base64 code: %s", err.Error())
	}
	err = runner.GetCkeNodeFromYaml(ckeNodesByte, ckeNodes)
	if err != nil {
		logrus.Errorf("error unmarshal ckeNodes: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeNodes: %s", err.Error())
	}

	var watchConfig = &ckeadm.WatchConfig{ListenAddress: ctx.String("address"), ListenPort: ctx.Int("port"),
		LogCacheDir: constants.CkeadmLogCacheDir, CluserID: ckeCluster.ObjectMeta.Name}
	return executor.CreateComponent(ckeComponents, ckeCluster, ckeNodes, keyFile, *watchConfig)
}

func componentScaleUpFromCli(ctx *cli.Context) error {
	logrus.Infof("running ckeadm component scale up...")
	logrus.SetLevel(logrus.Level(ctx.Int("v")))

	ckeClusterCode := ctx.String("cluster")
	ckeComponentCode := ctx.String("component")
	ckeNodeCode := ctx.String("node")
	keyFile := ctx.String("private-key")
	if keyFile == "" {
		keyFile = constants.DefaultKeyFile
	}

	var ckeCluster ckeadmv1beta1.CkeCluster
	ckeNodes := new([]ckeadmv1beta1.CkeNode)
	ckeComponents := new([]ckeadmv1beta1.CkeComponent)

	ckeClusterByte, err := base64.StdEncoding.DecodeString(ckeClusterCode)
	if err != nil {
		logrus.Errorf("error decode CkeCluster base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeCluster base64 code: %s", err.Error())
	}
	err = runner.GetCkeClusterFromYaml(ckeClusterByte, &ckeCluster)
	if err != nil {
		logrus.Errorf("error unmarshal ckeCluster: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeCluster: %s", err.Error())
	}

	ckeComponentByte, err := base64.StdEncoding.DecodeString(ckeComponentCode)
	if err != nil {
		logrus.Errorf("error decode CkeComponent base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeComponent base64 code: %s", err.Error())
	}
	err = runner.GetCkeComponentFromYaml(ckeComponentByte, ckeComponents)
	if err != nil {
		logrus.Errorf("error unmarshal ckeComponent: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeComponent: %s", err.Error())
	}

	ckeNodesByte, err := base64.StdEncoding.DecodeString(ckeNodeCode)
	if err != nil {
		logrus.Errorf("error decode CkeNode base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeNode base64 code: %s", err.Error())
	}
	err = runner.GetCkeNodeFromYaml(ckeNodesByte, ckeNodes)
	if err != nil {
		logrus.Errorf("error unmarshal ckeNodes: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeNodes: %s", err.Error())
	}

	addNode := ctx.String("nodeName")

	var watchConfig = &ckeadm.WatchConfig{ListenAddress: ctx.String("address"), ListenPort: ctx.Int("port"),
		LogCacheDir: constants.CkeadmLogCacheDir, CluserID: ckeCluster.ObjectMeta.Name}
	return executor.ScaleUpComponent(ckeComponents, ckeCluster, ckeNodes, keyFile, *watchConfig, addNode)
}

func componentScaleDownFromCli(ctx *cli.Context) error {
	logrus.Infof("running ckeadm component scale down...")
	logrus.SetLevel(logrus.Level(ctx.Int("v")))

	ckeClusterCode := ctx.String("cluster")
	ckeComponentCode := ctx.String("component")
	ckeNodeCode := ctx.String("node")
	keyFile := ctx.String("private-key")
	if keyFile == "" {
		keyFile = constants.DefaultKeyFile
	}

	var ckeCluster ckeadmv1beta1.CkeCluster
	ckeNodes := new([]ckeadmv1beta1.CkeNode)
	ckeComponents := new([]ckeadmv1beta1.CkeComponent)

	ckeClusterByte, err := base64.StdEncoding.DecodeString(ckeClusterCode)
	if err != nil {
		logrus.Errorf("error decode CkeCluster base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeCluster base64 code: %s", err.Error())
	}
	err = runner.GetCkeClusterFromYaml(ckeClusterByte, &ckeCluster)
	if err != nil {
		logrus.Errorf("error unmarshal ckeCluster: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeCluster: %s", err.Error())
	}

	ckeComponentByte, err := base64.StdEncoding.DecodeString(ckeComponentCode)
	if err != nil {
		logrus.Errorf("error decode CkeComponent base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeComponent base64 code: %s", err.Error())
	}
	err = runner.GetCkeComponentFromYaml(ckeComponentByte, ckeComponents)
	if err != nil {
		logrus.Errorf("error unmarshal ckeComponent: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeComponent: %s", err.Error())
	}

	ckeNodesByte, err := base64.StdEncoding.DecodeString(ckeNodeCode)
	if err != nil {
		logrus.Errorf("error decode CkeNode base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeNode base64 code: %s", err.Error())
	}
	err = runner.GetCkeNodeFromYaml(ckeNodesByte, ckeNodes)
	if err != nil {
		logrus.Errorf("error unmarshal ckeNodes: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeNodes: %s", err.Error())
	}

	var watchConfig = &ckeadm.WatchConfig{ListenAddress: ctx.String("address"), ListenPort: ctx.Int("port"),
		LogCacheDir: constants.CkeadmLogCacheDir, CluserID: ckeCluster.ObjectMeta.Name}
	return executor.ScaleDownComponent(ckeComponents, ckeCluster, ckeNodes, keyFile, *watchConfig)
}

func componentDeleteFromCli(ctx *cli.Context) error {
	logrus.Infof("running ckeadm component delete...")
	logrus.SetLevel(logrus.Level(ctx.Int("v")))

	ckeClusterCode := ctx.String("cluster")
	ckeComponentCode := ctx.String("component")
	ckeNodeCode := ctx.String("node")
	keyFile := ctx.String("private-key")
	if keyFile == "" {
		keyFile = constants.DefaultKeyFile
	}

	var ckeCluster ckeadmv1beta1.CkeCluster
	ckeNodes := new([]ckeadmv1beta1.CkeNode)
	ckeComponents := new([]ckeadmv1beta1.CkeComponent)

	ckeClusterByte, err := base64.StdEncoding.DecodeString(ckeClusterCode)
	if err != nil {
		logrus.Errorf("error decode CkeCluster base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeCluster base64 code: %s", err.Error())
	}
	err = runner.GetCkeClusterFromYaml(ckeClusterByte, &ckeCluster)
	if err != nil {
		logrus.Errorf("error unmarshal ckeCluster: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeCluster: %s", err.Error())
	}

	ckeComponentByte, err := base64.StdEncoding.DecodeString(ckeComponentCode)
	if err != nil {
		logrus.Errorf("error decode CkeComponent base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeComponent base64 code: %s", err.Error())
	}
	err = runner.GetCkeComponentFromYaml(ckeComponentByte, ckeComponents)
	if err != nil {
		logrus.Errorf("error unmarshal ckeComponent: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeComponent: %s", err.Error())
	}

	ckeNodesByte, err := base64.StdEncoding.DecodeString(ckeNodeCode)
	if err != nil {
		logrus.Errorf("error decode CkeNode base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeNode base64 code: %s", err.Error())
	}
	err = runner.GetCkeNodeFromYaml(ckeNodesByte, ckeNodes)
	if err != nil {
		logrus.Errorf("error unmarshal ckeNodes: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeNodes: %s", err.Error())
	}

	var watchConfig = &ckeadm.WatchConfig{ListenAddress: ctx.String("address"), ListenPort: ctx.Int("port"),
		LogCacheDir: constants.CkeadmLogCacheDir, CluserID: ckeCluster.ObjectMeta.Name}
	return executor.DeleteComponent(ckeComponents, ckeCluster, ckeNodes, keyFile, *watchConfig)
}

func componentUpgradeFromCli(ctx *cli.Context) error {
	logrus.Infof("running ckeadm component upgrade...")
	logrus.SetLevel(logrus.Level(ctx.Int("v")))

	ckeClusterCode := ctx.String("cluster")
	ckeComponentCode := ctx.String("component")
	ckeNodeCode := ctx.String("node")
	keyFile := ctx.String("private-key")
	if keyFile == "" {
		keyFile = constants.DefaultKeyFile
	}

	var ckeCluster ckeadmv1beta1.CkeCluster
	ckeNodes := new([]ckeadmv1beta1.CkeNode)
	ckeComponents := new([]ckeadmv1beta1.CkeComponent)

	ckeClusterByte, err := base64.StdEncoding.DecodeString(ckeClusterCode)
	if err != nil {
		logrus.Errorf("error decode CkeCluster base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeCluster base64 code: %s", err.Error())
	}
	err = runner.GetCkeClusterFromYaml(ckeClusterByte, &ckeCluster)
	if err != nil {
		logrus.Errorf("error unmarshal ckeCluster: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeCluster: %s", err.Error())
	}

	ckeComponentByte, err := base64.StdEncoding.DecodeString(ckeComponentCode)
	if err != nil {
		logrus.Errorf("error decode CkeComponent base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeComponent base64 code: %s", err.Error())
	}
	err = runner.GetCkeComponentFromYaml(ckeComponentByte, ckeComponents)
	if err != nil {
		logrus.Errorf("error unmarshal ckeComponent: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeComponent: %s", err.Error())
	}

	ckeNodesByte, err := base64.StdEncoding.DecodeString(ckeNodeCode)
	if err != nil {
		logrus.Errorf("error decode CkeNode base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeNode base64 code: %s", err.Error())
	}
	err = runner.GetCkeNodeFromYaml(ckeNodesByte, ckeNodes)
	if err != nil {
		logrus.Errorf("error unmarshal ckeNodes: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeNodes: %s", err.Error())
	}

	var watchConfig = &ckeadm.WatchConfig{ListenAddress: ctx.String("address"), ListenPort: ctx.Int("port"),
		LogCacheDir: constants.CkeadmLogCacheDir, CluserID: ckeCluster.ObjectMeta.Name}
	return executor.UpgradeComponent(ckeComponents, ckeCluster, ckeNodes, keyFile, *watchConfig)
}

func componentUpdateFromCli(ctx *cli.Context) error {
	logrus.Infof("running ckeadm component update...")
	logrus.SetLevel(logrus.Level(ctx.Int("v")))

	ckeClusterCode := ctx.String("cluster")
	ckeComponentCode := ctx.String("component")
	ckeNodeCode := ctx.String("node")
	keyFile := ctx.String("private-key")
	if keyFile == "" {
		keyFile = constants.DefaultKeyFile
	}

	var ckeCluster ckeadmv1beta1.CkeCluster
	ckeNodes := new([]ckeadmv1beta1.CkeNode)
	ckeComponents := new([]ckeadmv1beta1.CkeComponent)

	ckeClusterByte, err := base64.StdEncoding.DecodeString(ckeClusterCode)
	if err != nil {
		logrus.Errorf("error decode CkeCluster base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeCluster base64 code: %s", err.Error())
	}
	err = runner.GetCkeClusterFromYaml(ckeClusterByte, &ckeCluster)
	if err != nil {
		logrus.Errorf("error unmarshal ckeCluster: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeCluster: %s", err.Error())
	}

	ckeComponentByte, err := base64.StdEncoding.DecodeString(ckeComponentCode)
	if err != nil {
		logrus.Errorf("error decode CkeComponent base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeComponent base64 code: %s", err.Error())
	}
	err = runner.GetCkeComponentFromYaml(ckeComponentByte, ckeComponents)
	if err != nil {
		logrus.Errorf("error unmarshal ckeComponent: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeComponent: %s", err.Error())
	}

	ckeNodesByte, err := base64.StdEncoding.DecodeString(ckeNodeCode)
	if err != nil {
		logrus.Errorf("error decode CkeNode base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeNode base64 code: %s", err.Error())
	}
	err = runner.GetCkeNodeFromYaml(ckeNodesByte, ckeNodes)
	if err != nil {
		logrus.Errorf("error unmarshal ckeNodes: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeNodes: %s", err.Error())
	}

	var watchConfig = &ckeadm.WatchConfig{ListenAddress: ctx.String("address"), ListenPort: ctx.Int("port"),
		LogCacheDir: constants.CkeadmLogCacheDir, CluserID: ckeCluster.ObjectMeta.Name}
	return executor.UpdateComponent(ckeComponents, ckeCluster, ckeNodes, keyFile, *watchConfig)
}

func componentListFromCli(ctx *cli.Context) error {
	var templateComponents = new([]ckeadmv1beta1.CkeComponent)
	err := runner.GetCkeComponentForList(constants.CkeDeployConfigPath+"/component-template", templateComponents)
	if err != nil {
		logrus.Errorf("failed to read template ckeComponents from file, %s\n", err.Error())
		return err
	}

	var componentList = make(map[string]ckeadm.ComponentListInfo, 0)
	componentList["NAME"] = ckeadm.ComponentListInfo{Name: "NAME", Version: "VERSION", BaseComponent: "BASE COMPONENT", Depend: "DEPEND"}
	for _, component := range *templateComponents {
		componentList[component.ObjectMeta.Name] = ckeadm.ComponentListInfo{
			Name:          component.ObjectMeta.Name,
			Version:       component.Spec.Version,
			BaseComponent: component.ObjectMeta.Labels["baseComponent"],
			Depend:        strings.Join(component.Spec.Depend, ","),
		}
	}
	ShowComponentList(*templateComponents, componentList, os.Stdout)
	return nil
}

//make map[componentName][filename]
func componentExplainFromCli(ctx *cli.Context) error {
	componentName := ctx.String("name")
	var templateComponents = new([]ckeadmv1beta1.CkeComponent)
	fileMap, err := runner.GetCkeComponentForExplain(constants.CkeDeployConfigPath+"/component-template", templateComponents)
	if err != nil {
		logrus.Errorf("failed to read template ckeComponents from file, %s\n", err.Error())
		fmt.Printf("Failed to read template ckeComponents from file, %s\n", err.Error())
		return err
	}

	if _, ok := fileMap[componentName]; !ok {
		fmt.Printf("Template of component %s not found.\n", componentName)
		return nil
	}

	//print file
	data, err := ioutil.ReadFile(fileMap[componentName])
	if err != nil {
		fmt.Printf("Read template file %s failed: %s.\n", fileMap[componentName], err.Error())
		return nil
	}
	fmt.Println(string(data))
	return nil

	//componentName := ctx.String("name")
	//var templateComponents = new([]ckeadmv1beta1.CkeComponent)
	//err := runner.GetCkeComponentFromFile(constants.CkeDeployConfigPath+"/component-template", templateComponents)
	//if err != nil {
	//	logrus.Errorf("failed to read template ckeComponents from file, %s\n", err.Error())
	//	fmt.Printf("Failed to read template ckeComponents from file, %s\n", err.Error())
	//	return err
	//}
	//
	//for _, component := range *templateComponents {
	//	if component.ObjectMeta.Name == componentName {
	//		yamlBytes, err := runner.ComponentToYaml(component)
	//		if err != nil {
	//			logrus.Errorf("Marshal component failed: %s", err.Error())
	//			return err
	//		}
	//		fmt.Printf("%s\n", string(yamlBytes))
	//		return nil
	//	}
	//}
	//
	//fmt.Printf("Template of component %s not found.\n", componentName)
	//return nil
}

func componentStatusFromCli(ctx *cli.Context) error {
	componentName := ctx.String("c")
	nodeName := ctx.String("n")
	clusterId := ctx.String("clusterID")
	action := ctx.String("action")
	logrus.Infof("Show log messages for component %s action %s ...", componentName, action)
	dirName := fmt.Sprintf("%s/%s/%s/%s", constants.CkeComponentLogPath, clusterId, componentName, action)
	var err error
	var logFile string
	if nodeName != "" {
		logFile, err = util.GetLatestFileInPathWithName(dirName, nodeName)
	} else {
		logFile, err = util.GetLatestFileInPath(dirName)
	}
	if err != nil {
		logrus.Errorf("failed to get latest log file: %s", err.Error())
		return err
	}

	file, err := os.Open(filepath.Clean(logFile))
	if err != nil {
		logrus.Errorf("failed to open log file: %s", err.Error())
		return err
	}
	r := bufio.NewReader(file)
	for {
		by, err := r.ReadBytes('\n')
		if err != nil && err != io.EOF {
			logrus.Errorf("read log file error: %s", err.Error())
			return err
		}
		fmt.Print(string(by))
		if err == io.EOF {
			time.Sleep(time.Second)
		}
	}
	// infinite loop
}

/*
func componentMaster2SlaveFromCli(ctx *cli.Context) error {
	logrus.Infof("running ckeadm component master2slave...")
	logrus.SetLevel(logrus.Level(ctx.Int("v")))

	ckeClusterCode := ctx.String("cluster")
	ckeComponentCode := ctx.String("component")
	ckeNodeCode := ctx.String("node")
	keyFile := ctx.String("private-key")
	if keyFile == "" {
		keyFile = constants.DefaultKeyFile
	}
	var ckeCluster ckeadmv1beta1.CkeCluster
	ckeNodes := new([]ckeadmv1beta1.CkeNode)
	ckeComponents := new([]ckeadmv1beta1.CkeComponent)

	ckeClusterByte, err := base64.StdEncoding.DecodeString(ckeClusterCode)
	if err != nil {
		logrus.Errorf("error decode CkeCluster base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeCluster base64 code: %s", err.Error())
	}
	err = runner.GetCkeClusterFromYaml(ckeClusterByte, &ckeCluster)
	if err != nil {
		logrus.Errorf("error unmarshal ckeCluster: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeCluster: %s", err.Error())
	}

	ckeComponentByte, err := base64.StdEncoding.DecodeString(ckeComponentCode)
	if err != nil {
		logrus.Errorf("error decode CkeComponent base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeComponent base64 code: %s", err.Error())
	}
	err = runner.GetCkeComponentFromYaml(ckeComponentByte, ckeComponents)
	if err != nil {
		logrus.Errorf("error unmarshal ckeComponent: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeComponent: %s", err.Error())
	}

	ckeNodesByte, err := base64.StdEncoding.DecodeString(ckeNodeCode)
	if err != nil {
		logrus.Errorf("error decode CkeNode base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeNode base64 code: %s", err.Error())
	}
	err = runner.GetCkeNodeFromYaml(ckeNodesByte, ckeNodes)
	if err != nil {
		logrus.Errorf("error unmarshal ckeNodes: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeNodes: %s", err.Error())
	}

	var watchConfig = &ckeadm.WatchConfig{ListenAddress: ctx.String("address"), ListenPort: ctx.Int("port"),
		LogCacheDir: constants.CkeadmLogCacheDir, CluserID: ckeCluster.ObjectMeta.Name}
	return executor.Master2SlaveComponent(ckeComponents, ckeCluster, ckeNodes, keyFile, *watchConfig)
}

func componentSlave2MasterFromCli(ctx *cli.Context) error {
	logrus.Infof("running ckeadm component slave2master...")
	logrus.SetLevel(logrus.Level(ctx.Int("v")))

	ckeClusterCode := ctx.String("cluster")
	ckeComponentCode := ctx.String("component")
	ckeNodeCode := ctx.String("node")
	keyFile := ctx.String("private-key")
	if keyFile == "" {
		keyFile = constants.DefaultKeyFile
	}
	var ckeCluster ckeadmv1beta1.CkeCluster
	ckeNodes := new([]ckeadmv1beta1.CkeNode)
	ckeComponents := new([]ckeadmv1beta1.CkeComponent)

	ckeClusterByte, err := base64.StdEncoding.DecodeString(ckeClusterCode)
	if err != nil {
		logrus.Errorf("error decode CkeCluster base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeCluster base64 code: %s", err.Error())
	}
	err = runner.GetCkeClusterFromYaml(ckeClusterByte, &ckeCluster)
	if err != nil {
		logrus.Errorf("error unmarshal ckeCluster: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeCluster: %s", err.Error())
	}

	ckeComponentByte, err := base64.StdEncoding.DecodeString(ckeComponentCode)
	if err != nil {
		logrus.Errorf("error decode CkeComponent base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeComponent base64 code: %s", err.Error())
	}
	err = runner.GetCkeComponentFromYaml(ckeComponentByte, ckeComponents)
	if err != nil {
		logrus.Errorf("error unmarshal ckeComponent: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeComponent: %s", err.Error())
	}

	ckeNodesByte, err := base64.StdEncoding.DecodeString(ckeNodeCode)
	if err != nil {
		logrus.Errorf("error decode CkeNode base64 code: %s", err.Error())
		return fmt.Errorf("error decode CkeNode base64 code: %s", err.Error())
	}
	err = runner.GetCkeNodeFromYaml(ckeNodesByte, ckeNodes)
	if err != nil {
		logrus.Errorf("error unmarshal ckeNodes: %s", err.Error())
		return fmt.Errorf("error unmarshal ckeNodes: %s", err.Error())
	}

	var watchConfig = &ckeadm.WatchConfig{ListenAddress: ctx.String("address"), ListenPort: ctx.Int("port"),
		LogCacheDir: constants.CkeadmLogCacheDir, CluserID: ckeCluster.ObjectMeta.Name}
	return executor.Slave2MasterComponent(ckeComponents, ckeCluster, ckeNodes, keyFile, *watchConfig)
}
*/

func ShowComponentList(components []ckeadmv1beta1.CkeComponent, listInfo map[string]ckeadm.ComponentListInfo, writer io.Writer) {
	const LenMax = 150
	var nameMax, versionMax, baseComponentMax, dependMax int
	for _, row := range listInfo {
		if nameMax < len(row.Name) && len(row.Name) < LenMax {
			nameMax = len(row.Name)
		}
		if versionMax < len(row.Version) && len(row.Version) < LenMax {
			versionMax = len(row.Version)
		}
		if baseComponentMax < len(row.BaseComponent) && len(row.BaseComponent) < LenMax {
			baseComponentMax = len(row.BaseComponent)
		}
		if dependMax < len(row.Depend) && len(row.Depend) < LenMax {
			dependMax = len(row.Depend)
		}
	}

	_, _ = fmt.Fprintf(writer, "%s   %s   %s   %s\n",
		ToLength(listInfo["NAME"].Name, nameMax),
		ToLength(listInfo["NAME"].Version, versionMax),
		ToLength(listInfo["NAME"].BaseComponent, baseComponentMax),
		ToLength(listInfo["NAME"].Depend, dependMax),
	)
	for _, component := range components {
		_, _ = fmt.Fprintf(writer, "%s   %s   %s   %s\n",
			ToLength(listInfo[component.Name].Name, nameMax),
			ToLength(listInfo[component.Name].Version, versionMax),
			ToLength(listInfo[component.Name].BaseComponent, baseComponentMax),
			ToLength(listInfo[component.Name].Depend, dependMax),
		)
	}
}

func ToLength(str string, le int) string {
	if len(str) >= le {
		return str[:le]
	}
	for i := 0; len(str) < le; i++ {
		str += " "
	}
	return str
}
